//
//  LLTCameraViewModel.swift
//  TakePhoto
//
//  Created by 李陆涛 on 2018/7/24.
//  Copyright © 2018年 llt. All rights reserved.
//

import UIKit
import AVFoundation
import Photos
import CoreMotion

enum CameraPosition {
    
    case back
    case front
}

enum FlashModel {
    
    case on
    case off
}

enum ErrorType {
    
    case initDeviceError(msg:String?)
    case otherError(msg:String?)
}



protocol LLTCameraViewModelDelegate: NSObjectProtocol {
    
    func takePhtotFinish(image:UIImage?,error:ErrorType?)
    func videoRecordStart()
    func videoRecordFinish(videourl:URL?, error:ErrorType?)
    func videoRecordTime(time:Float,progress:Float)
    
    func flashModel(_ model:FlashModel)
}

class LLTCameraViewModel: NSObject {
    
    //    private var cameraStyle:CameraStyle = .clickVideo
    
    private var flashMode:AVCaptureDevice.FlashMode = .off {
        
        didSet {
            
            delegate?.flashModel((flashMode == .on) ? .on : .off)
        }
        
    }
    private var desiredPosition:AVCaptureDevice.Position = .back
    
    //    会话
    private var session:AVCaptureSession!
    //    输入
    private var deviceIntput:AVCaptureDeviceInput!
    //    输出
    private let imageOutPut: AVCaptureStillImageOutput = AVCaptureStillImageOutput()
    private let movieFileOutput:AVCaptureMovieFileOutput = AVCaptureMovieFileOutput()
    
    private var device: AVCaptureDevice?
    private var previewLayer: AVCaptureVideoPreviewLayer?
    
    private var timer: Timer?
    
    private var motionManager: CMMotionManager?
    private var orientation: AVCaptureVideoOrientation = .portrait
    
    
    
    /// 最大拍摄时长,不设置时
    /// videoRecordTime(time:Float,progress:Float)
    /// 中的 progress 为0
    var maxDuration:Float = 15 {
        
        didSet {
            
            movieFileOutput.maxRecordedDuration = CMTimeMake(Int64(maxDuration * 600), 600)
        }
    }
    
    weak var delegate: LLTCameraViewModelDelegate?
    
    
    var zoomFactor:CGFloat? {
        
        didSet {
            
            guard let device = device else { return }
            
            guard var zoom = zoomFactor else { return }
            
            let maxZoom = device.activeFormat.videoMaxZoomFactor - 10
            
            if zoom > maxZoom {
                
                zoom = maxZoom
            }
            
            if zoom < 1.0 {
                zoom = 1.0
            }
            
            try! device.lockForConfiguration()
            
            
            device.ramp(toVideoZoomFactor: zoom, withRate: 10)
            
            device.unlockForConfiguration()
        }
    }
    
    /// 初始化设备
    ///
    /// - Parameter view: 要显示成像的界面
    func setupWithPreview(_ view:UIView) {
        
        let actView = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
        
        actView.center = view.center
        view.addSubview(actView)
        actView.startAnimating()
        
        self.session = AVCaptureSession()
        
        DispatchQueue.global().async {
            
            [weak self] in
            guard let `self` = self else { return }
            
            self.setupAudioInput()
            self.setupVideoInput()
            self.setupOutput()
//            self.setupMonton()
            
            DispatchQueue.main.async {
                
                self.previewLayer = AVCaptureVideoPreviewLayer.init(session: self.session)
                self.previewLayer!.videoGravity = .resizeAspectFill
                self.previewLayer!.frame = view.bounds
                view.layer.addSublayer(self.previewLayer!)
                self.startRunning()
                
                actView.stopAnimating()
                actView.removeFromSuperview()
            }
        }
    }
    
    
    private func setupMonton() {
        
        try! AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
        try! AVAudioSession.sharedInstance().setActive(true)
        
        self.motionManager = CMMotionManager()
        self.motionManager?.deviceMotionUpdateInterval = 0.5
        
        if self.motionManager!.isDeviceMotionAvailable == false {
            
            self.motionManager = nil
            return
        }
        
        self.motionManager?.startDeviceMotionUpdates(to: OperationQueue.main, withHandler: {
            
            [weak self] motion, error in
            guard let `self` = self else { return }
            
            self.perform(#selector(self.handleDeviceMotion(motion:)), on: Thread.main, with: motion, waitUntilDone: true)
        })
    }
    
    @objc private func handleDeviceMotion(motion:CMDeviceMotion) {
        
        let x = motion.gravity.x
        let y = motion.gravity.y
        
        if fabs(x) >= fabs(y) {
            
            if y >= 0 {
                
                orientation = .portraitUpsideDown
            } else {
                
                orientation = .portrait
            }
        } else {
            
            if x >= 0 {
                
                orientation = .landscapeLeft
            } else {
                
                orientation = .landscapeRight
            }
        }
    }
    
    func startRunning() {
        
        session.startRunning()
        
    }
    
    func stopRunning()  {
        
        session.stopRunning()
    }
    
    deinit {
        
        self.motionManager?.stopDeviceMotionUpdates()
    }
    
    //    -------------------------初始化相机-------------------------
    
    //    配置输入源
    
    private func setupAudioInput() {
        
        guard let audioDevice = AVCaptureDevice.default(for: .audio) else {
            
            return
        }
        
        let audioInput = try! AVCaptureDeviceInput(device: audioDevice)
        
        if session.canAddInput(audioInput) {
            
            session.addInput(audioInput)
        }
    }
    
    private func setupVideoInput() {
        
        for aDevice in AVCaptureDevice.devices(for: .video) {
            
            if aDevice.position == desiredPosition {
                
                self.device = aDevice
                break
            }
        }
        
        guard let device = self.device else {
            return
        }
        
        try! device.lockForConfiguration()
        
        if (device.hasFlash) {
            device.flashMode = flashMode
        }
        if device.isFocusModeSupported(.autoFocus) {
            
            device.focusMode = .autoFocus
        }
        
        if device.isWhiteBalanceModeSupported(.autoWhiteBalance) {
            device.whiteBalanceMode = .autoWhiteBalance
        }
        if device.isExposurePointOfInterestSupported {
            device.exposureMode = .continuousAutoExposure
        }
        device.unlockForConfiguration()
        
        if deviceIntput != nil {
            session.removeInput(deviceIntput)
        }
        
        deviceIntput = try! AVCaptureDeviceInput(device: device)
        
        if session.canAddInput(deviceIntput) {
            session.addInput(deviceIntput)
        }
    }
    //    配置输出源
    private func setupOutput() {
        
        imageOutPut.outputSettings = [AVVideoCodecKey:AVVideoCodecJPEG, AVVideoScalingModeKey:AVVideoScalingModeResize]
        
        if session.canAddOutput(imageOutPut) {
            session.addOutput(imageOutPut)
        }
        
        if session.canAddOutput(movieFileOutput) {
            session.addOutput(movieFileOutput)
        }
        session.sessionPreset = .high
    }
    
    //    -------------------------相机操作-------------------------
    /// 点击聚焦
    ///
    /// - Parameter point: 点击的位置
    func foucsAction(_ point:CGPoint) {
        
        guard let device = deviceIntput?.device else { return }
        
        if device.position == .front {
            return
        }
        
        guard let pointInCamera = previewLayer?.captureDevicePointConverted(fromLayerPoint: point) else { return }
        
        try! device.lockForConfiguration()
        
        if device.isAutoFocusRangeRestrictionSupported {
            
            device.focusPointOfInterest = pointInCamera
        }
        if device.isFocusModeSupported(.autoFocus) {
            device.focusMode = .autoFocus
        }
        if device.isExposurePointOfInterestSupported {
            device.exposureMode = .continuousAutoExposure
            device.exposurePointOfInterest = pointInCamera
        }
        device.isSubjectAreaChangeMonitoringEnabled = true
        device.focusPointOfInterest = pointInCamera
        device.unlockForConfiguration()
    }
    
    /// 切换摄像头
    ///
    /// - Returns: （哪个摄像头，是否成功）
    func changeCameraPositionAction() -> (position:CameraPosition,success:Bool) {
        
        guard let device = deviceIntput?.device else { return (.back,false) }
        
        var position: CameraPosition = .back
        
        if device.position == .front {
            desiredPosition = .back
        } else {
            desiredPosition = .front
            position = .front
        }
        
        DispatchQueue.global().async {
            
            [weak self] in
            guard let `self` = self else { return }
            
            self.setupVideoInput()
        }
        
        return (position,true)
    }
    
    /// 手电筒 录像时用
    ///
    /// - Parameter model: 手电筒模式
    func changeTorchMode(_ model:FlashModel? = nil) {
        
        guard let device = deviceIntput?.device else { return }
        
        if device.isTorchAvailable {
            
            try! device.lockForConfiguration()
            
            var newModel:FlashModel = self.flashMode == .on ? FlashModel.off : FlashModel.on
            
            if let model = model {
                
                newModel = model
            }
            
            switch newModel {
                
            case .on:
                
                device.torchMode = .on
                flashMode = .on
                
            case .off:
                
                device.torchMode = .off
                flashMode = .off
            }
            
            device.unlockForConfiguration()
        }
    }
    
    
    /// 闪光灯 拍照时用
    ///
    /// - Parameter model: 闪光灯模式
    func changeFlashMode(_ model:FlashModel? = nil) {
        
        guard let device = deviceIntput?.device else { return }
        
        if device.isFlashAvailable {
            
            try! device.lockForConfiguration()
            
            
            var newModel:FlashModel = self.flashMode == .on ? FlashModel.off : FlashModel.on
            
            if let model = model {
                
                newModel = model
            }

            switch newModel {
                
            case .on:
                
                device.flashMode = .on
                flashMode = .on
                
            case .off:
                
                device.flashMode = .off
                flashMode = .off
            }
            
            device.unlockForConfiguration()
        }
    }
    
    
    //    -------------------------拍摄逻辑-------------------------
    
    
    /// 拍照
    ///
    /// - Parameter completionHandler: 照片或错误信息
    func takePhoto() {
        
        guard let stillImageConnection = imageOutPut.connection(with: .video) else {
            
            //            completionHandler(nil,ErrorType.initDeviceError(msg: "初始化失败"))
            
            self.delegate?.takePhtotFinish(image: nil,error:ErrorType.otherError(msg: "初始化失败"))
            return
            
        }
        
//        if stillImageConnection.isVideoOrientationSupported {
//
//            stillImageConnection.videoOrientation = self.orientation
//        }
        
        stillImageConnection.videoScaleAndCropFactor = 1
        
        imageOutPut.captureStillImageAsynchronously(from: stillImageConnection) {
            
            [weak self] buffer, error in
            guard let `self` = self else { return }
            
            guard let buffer = buffer else {
                
                //                completionHandler(nil,ErrorType.otherError(msg: error?.localizedDescription))
                
                self.delegate?.takePhtotFinish(image: nil, error: ErrorType.otherError(msg: error?.localizedDescription))
                return
                
            }
            
            guard let jpegData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer) else {
                
                self.delegate?.takePhtotFinish(image: nil,error:ErrorType.otherError(msg: "写入失败"))
                return
                
            }
            
            if let image = UIImage(data: jpegData) {
                
                self.delegate?.takePhtotFinish(image: image,error:nil)
                
            }
            
            self.delegate?.takePhtotFinish(image: nil,error:ErrorType.otherError(msg: "未知错误"))
        }
    }
    
    
    /// 开始录像
    func startRecording() {
        
        if movieFileOutput.isRecording {
            
            return
        }
        
        guard let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first else {
            
            
            delegate?.videoRecordFinish(videourl: nil, error: ErrorType.otherError(msg: "初始化失败"))
            
            return
        }
        let filePath = "\(path)/temp.mp4"
        
        guard let movieConnection = movieFileOutput.connection(with: AVMediaType.video) else {
            
            delegate?.videoRecordFinish(videourl: nil, error: ErrorType.otherError(msg: "初始化失败"))
            return
        }
        
//        movieConnection.videoOrientation = self.orientation
        movieConnection.videoScaleAndCropFactor = zoomFactor ?? 1.0
        
        movieFileOutput.startRecording(to: URL(fileURLWithPath: filePath), recordingDelegate: self)
    }
    
    /// 停止录像
    func stopRecording() {
        
        if movieFileOutput.isRecording {
            movieFileOutput.stopRecording()
        }
    }
    
    ///  保存视频到相册
    ///
    /// - Parameters:
    ///   - outputFileURL: 视频缓存链接
    ///   - completionHandler: 成功/错误
    func saveVideoToLibary(outputFileURL:URL,completionHandler: @escaping (Bool,ErrorType?) -> Swift.Void) {
        
        PHPhotoLibrary.shared().performChanges({
            
            PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputFileURL)
        }) { (success, error) in
            
            completionHandler(success,ErrorType.otherError(msg: error?.localizedDescription))
        }
    }
}

extension LLTCameraViewModel : AVCaptureFileOutputRecordingDelegate {
    
    
    func fileOutput(_ output: AVCaptureFileOutput, didStartRecordingTo fileURL: URL, from connections: [AVCaptureConnection]) {
        
        delegate?.videoRecordStart()
        startTimer()
    }
    
    func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
        
        delegate?.videoRecordFinish(videourl: outputFileURL, error: ErrorType.otherError(msg: error?.localizedDescription))
        
        stopTimer()
    }
    
    
    private func startTimer() {
        
        timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
    }
    
    private func stopTimer() {
        
        timer?.invalidate()
        timer = nil
        
        self.delegate?.videoRecordTime(time: 0,progress:0)
    }
    
    @objc private func timerAction() {
        
        if !movieFileOutput.isRecording {
            
            return
        }
        
        let currentDuration = CMTimeGetSeconds(movieFileOutput.recordedDuration)

        var progress = Float(currentDuration) / maxDuration
        
        if progress >= 1.0 {
            
            progress = 1.0
        }
        
        self.delegate?.videoRecordTime(time: Float(currentDuration),progress:Float(progress))
    }
}


